﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Falcon.Core.Helpers
{
	using System.Globalization;
	using System.IO;
	using System.Text;
	using System.Web.Routing;
	using System.Web.UI;
	using Falcon.Core.Collections;
	using Falcon.Core.Domain;
	using Falcon.Core.Extensions;

	public class PagerHelper<T, TK>
	{
		// private constants
		private const int Adjacents = 1;

		/// <summary>
		/// Initializes a new instance of the <see cref="PagerHelper&lt;T, TK&gt;"/> class.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="items">The items.</param>
		/// <param name="route">The route.</param>
		public PagerHelper(RequestContext context, IPagedList<T, TK> items, string route)
		{
			this.Context = context;
			this.Items = items;
			this.Route = route;
		}

		/// <summary>
		/// Gets or sets the context.
		/// </summary>
		/// <value>The context.</value>
		public RequestContext Context
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the items.
		/// </summary>
		/// <value>The items.</value>
		public IPagedList<T, TK> Items
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the route.
		/// </summary>
		/// <value>The route.</value>
		public string Route
		{
			get;
			private set;
		}

		/// <summary>
		/// Renders this instance.
		/// </summary>
		/// <returns></returns>
		public string Render()
		{
			if (this.Items != null && this.Items.Total > 0)
			{
				var builder = new StringBuilder();

				using (var writer = new StringWriter(builder, CultureInfo.InvariantCulture))
				{
					using (var html = new XhtmlTextWriter(writer))
					{
						var route = RouteTable.Routes[this.Route];

						html.AddAttribute(HtmlTextWriterAttribute.Class, "pagination");
						html.RenderBeginTag(HtmlTextWriterTag.Div);

						html.Indent++;

						html.AddAttribute(HtmlTextWriterAttribute.Class, "results");
						html.RenderBeginTag(HtmlTextWriterTag.Div);

						html.Indent++;

						html.RenderBeginTag(HtmlTextWriterTag.Span);
						html.Write(Resources.Messages.ShowingResults.Fmt(this.Items.StartIndex, this.Items.EndIndex, this.Items.Total));
						html.RenderEndTag();

						html.Indent--;

						html.RenderEndTag();

						html.WriteLine();

						html.AddAttribute(HtmlTextWriterAttribute.Class, "pager");
						html.RenderBeginTag(HtmlTextWriterTag.Div);

						html.Indent++;

						if (this.Items.HasPrev)
						{
							var url = route.GetVirtualPath(
								this.Context, 
								new RouteValueDictionary(new { page = this.Items.PrevPage })).VirtualPath;

							html.AddAttribute(HtmlTextWriterAttribute.Href, url.AsRoot());
							html.RenderBeginTag(HtmlTextWriterTag.A);
							html.Write("&laquo; prev");
							html.RenderEndTag();
						}
						else
						{
							html.AddAttribute(HtmlTextWriterAttribute.Class, "disabled");
							html.RenderBeginTag(HtmlTextWriterTag.Span);
							html.Write("&laquo; prev");
							html.RenderEndTag();
						}

						html.WriteLine();

						if (this.Items.Pages < (4 + (2 * 2)))
						{
							WriteNumberedLinks(html, route, 1, this.Items.Pages);
						}
						else
						{
							if ((this.Items.Pages - (Adjacents * 2) > this.Items.Page) && (this.Items.Page > (Adjacents * 2)))
							{
								WriteNumberedLinks(html, route, 1, 2);
								WriteElipsis(html);
								WriteNumberedLinks(html, route, this.Items.Page - Adjacents, this.Items.Page + Adjacents);
								WriteElipsis(html);
								WriteNumberedLinks(html, route, this.Items.Pages - 1, this.Items.Pages);
							}
							else if (this.Items.Page < (this.Items.Pages / 2))
							{
								WriteNumberedLinks(html, route, 1, 2 + (Adjacents * 2));
								WriteElipsis(html);
								WriteNumberedLinks(html, route, this.Items.Pages - 1, this.Items.Pages);
							}
							else
							{
								WriteNumberedLinks(html, route, 1, 2);
								WriteElipsis(html);
								WriteNumberedLinks(html, route, this.Items.Pages - (2 + (Adjacents * 2)), this.Items.Pages);
							}
						}

						if (this.Items.HasNext)
						{
							var url = route.GetVirtualPath(
								this.Context,
								new RouteValueDictionary(new { page = this.Items.NextPage })).VirtualPath;

							html.AddAttribute(HtmlTextWriterAttribute.Href, url.AsRoot());
							html.RenderBeginTag(HtmlTextWriterTag.A);
							html.Write("next &laquo;");
							html.RenderEndTag();
						}
						else
						{
							html.AddAttribute(HtmlTextWriterAttribute.Class, "disabled");
							html.RenderBeginTag(HtmlTextWriterTag.Span);
							html.Write("next &laquo;");
							html.RenderEndTag();
						}

						html.Indent--;

						html.RenderEndTag();

						html.Indent--;

						html.RenderEndTag();
					}
				}

				return builder.ToString();
			}

			return string.Empty;
		}

		/// <summary>
		/// Writes the numbered links.
		/// </summary>
		/// <param name="html">The HTML.</param>
		/// <param name="route">The route.</param>
		/// <param name="start">The start.</param>
		/// <param name="end">The end.</param>
		private void WriteNumberedLinks(HtmlTextWriter html, RouteBase route, int start, int end)
		{
			for (var i = start; i <= end; i++)
			{
				WriteNumberedLink(html, route, i);
			}
		}

		/// <summary>
		/// Writes the numbered link.
		/// </summary>
		/// <param name="html">The HTML.</param>
		/// <param name="route">The route.</param>
		/// <param name="index">The index.</param>
		private void WriteNumberedLink(HtmlTextWriter html, RouteBase route, int index)
		{
			if (this.Items.Page == index)
			{
				html.AddAttribute(HtmlTextWriterAttribute.Class, "current");
				html.RenderBeginTag(HtmlTextWriterTag.Span);
				html.Write(index);
				html.RenderEndTag();
			}
			else
			{
				var url = route.GetVirtualPath(
					this.Context,
					new RouteValueDictionary(new { page = index })).VirtualPath;

				html.AddAttribute(HtmlTextWriterAttribute.Href, url.AsRoot());
				html.RenderBeginTag(HtmlTextWriterTag.A);
				html.Write(index);
				html.RenderEndTag();
			}

			html.WriteLine();
		}

		/// <summary>
		/// Writes the elipsis.
		/// </summary>
		/// <param name="html">The HTML.</param>
		private static void WriteElipsis(HtmlTextWriter html)
		{
			html.RenderBeginTag(HtmlTextWriterTag.Span);
			html.Write("&#8230;");
			html.RenderEndTag();
		}
	}
}